package com.hongbao.game.service;

import com.hongbao.game.model.BatchPrize;
import com.hongbao.game.model.Game;
import com.hongbao.game.model.Prize;
import com.hongbao.game.model.User;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Random;

@Service
@Scope(value="singleton")
//request 每一次请求一个对象
//session 每一次回话一个对象
//prototype  每一次调用都创建一个对象
public class GameService_Lock {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    PrizeService prizeService;

    public Game findById(Long id){
        //从数据库查询
        Date now = new Date();
        Game game = new Game(1L,"Rain",now,
                new Date(now.getTime()+120*1000),1);
        return game;
    }


    public void attachPrize(Game game, List<BatchPrize> batchPrizeList) {
        List<Prize> prizeList = shufflePrizeAndGenerateTimeStamp(game, batchPrizeList);

        //在redis中建立关联
        //game 和 关联的奖品
        for (Prize prize : prizeList) {
            redisTemplate.opsForList().rightPush("game_prize_"+game.getId(),prize);
        }
    }


    /**
     * 抽中返回Prize
     * 否则返回null
     * @param gameId
     * @param user
     * @return
     *
     * 1、并发思维，分布式思维
     *   抽象：共享资源
     *         锁
     *         Java  synchronized,因为任何对象上都关联了一个锁 monitor   --->对象锁
     *                同步方法 锁住当前对象
     *                同步块 synchronized（lock）获取括号里面的对象的锁
     *
     *                任何一个Class 上都关联一个锁    -->类锁
     *                静态同步方法 锁住当前类
     *                synchronized（ClassName.class)
     *
     *         乐观锁
     *         数据库实现方式
     */
    @Autowired
    OptimisticLock_DB optimisticLock_db;

    @Autowired
    PessimisticLock_DB pessimisticLock_db;

    @Autowired
    RedissonClient redissonClient;
    //0.高可用性  集群
    //1. 分布式  redis

    //1.互斥性
    //2.可重入性
    //5.自动重试机制 自旋
    //6 解铃还须系铃人 threadId
    //3.防止死锁 设置过期时间
    //4.续期  防止执行时间过长 看门狗 守护线程
    public synchronized  String takePartGame(Long gameId, User user) {

        long id = Thread.currentThread().getId();
        System.out.println("ThreadId=" + id);
        RLock lock = redissonClient.getLock("1905A");
        try {
            lock.lock();//加锁
            /*
            try {
                Thread.sleep(40*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/

            Game game = findById(gameId);
            Date now1 = new Date();
            boolean now_before_startTime = now1.before(game.getStartTime());
            if (now_before_startTime) {
                return "活动尚未开始";
            }

            boolean now_after_endTime = now1.after(game.getEndTime());
            if (now_after_endTime) {
                return "活动已经结束";
            }
            Integer user_limit = (Integer) redisTemplate.opsForHash().get("game_limit_" + gameId, user.getLevel());

            // 从此处开始Lua脚本
            Integer userNum =Integer.parseInt ((String) redisTemplate.opsForHash().get("game_user_" + gameId, user.getId()));
            if (userNum != null) {
                if (userNum >= user_limit) {
                    return "抽奖次数已到 " + user_limit;
                }
            } else {
                userNum = 0;
            }

            Prize prize = (Prize) redisTemplate.opsForList().leftPop("game_prize_" + gameId);
            if (prize == null) {
                return "已经抢完了";
            }
            Long now = System.currentTimeMillis();
            if (now > prize.getTimestampMS()) {
                redisTemplate.opsForHash().put("game_user_" + gameId, user.getId(), String.valueOf(userNum + 1));
                return prize.toString();
            } else {//红包雨没下来
                System.out.println("重新放回去" + prize);
                redisTemplate.opsForList().leftPush("game_prize_" + gameId, prize);
            }
            return "没有抢到";

        }finally {
            lock.unlock();
        }
    }


    public void method1(){
        RLock lock = redissonClient.getLock("1905A");
        try {
            lock.lock();//加锁
            System.out.println("method1()");
        }finally {
            lock.unlock();
        }
    }

    /**
     * 打散所有的批次的奖品
     * 并且生成时间戳
     * @param game
     * @param batchPrizeList
     * @return
     */
    public List<Prize> shufflePrizeAndGenerateTimeStamp(Game game,
                                      List<BatchPrize> batchPrizeList){

        List<Prize> prizeList = prizeService.shufflePrize(batchPrizeList);
        if(game.getType()==1){
            generateTimeStamp(game,prizeList);
        }
        return prizeList;
    }

    private void generateTimeStamp(Game game,
            List<Prize> prizeList){
        Date startTime = game.getStartTime();
        Date endTime = game.getEndTime();

        if(game.getType() == 1){
            Integer hongbao_count = prizeList.size();//hongbao数量
            Long hongbao_time_ms = endTime.getTime() - startTime.getTime();//时间ms

            int interval = (int)Math.ceil(hongbao_time_ms/hongbao_count);

            for(int i=0;i<hongbao_count;i++){
                Random random = new Random();
                int i1 = random.nextInt(interval);
                Prize prize = prizeList.get(i);
                prize.setTimestampMS(startTime.getTime()+i*interval + i1);
            }

            prizeList.forEach((e)->{
                System.out.println(e);
            });
        }
    }

}
